package com.android.mangalib.parser;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import com.android.mangalib.constant.Constant;
import com.android.mangalib.object.Collection;
import com.android.mangalib.object.Tome;

import android.content.Context;
import android.content.res.AssetManager;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class XMLParser {
	
	/**
	 * Load the XML and parse it as a Document
	 * 
	 * @param contextParam
	 * @return Document
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	private static Document loadXmlAsDocument(Context contextParam) throws ParserConfigurationException, SAXException, IOException {
	
		// Create the asset manager to get the file
		DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
		documentBuilderFactory.setNamespaceAware(true);
		DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
		Document document = null;
		AssetManager assetManager = contextParam.getAssets();
				
		try {
			// Open the data file and parse it as a Document
			document = builder.parse(assetManager.open(Constant.MANGALIB_DATA_FILE));
		}
		catch (Exception e) {
			System.out.println(e.getMessage());
			
		}
		return document;
	}
	
	/**
	 * Get all collections in order to display them in the main Activity
	 * 
	 * @param contextParam
	 * @return listCollections
	 * @throws XPathExpressionException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	public static List<Collection> getAllCollections(Context contextParam) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException {
		
		Document document = loadXmlAsDocument(contextParam);
		XPath xpath = XPathFactory.newInstance().newXPath();
		// Get all Collection Node
		String expression = Constant.COLLECTIONS_NODE + Constant.SEPARATOR_NODE + Constant.COLLECTION_NODE;
		NodeList nodeList = (NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
		
		List<Collection> listCollections = new ArrayList<Collection>();
		// Create the list of Collection from the data
		for (int i = 0 ; i < nodeList.getLength() ; i++ ) {
			listCollections.add(new Collection(nodeList.item(i).getAttributes().item(1).getNodeValue(), nodeList.item(i).getAttributes().item(0).getNodeValue()));
		}
		
		return listCollections;
		
	}
	
	/**
	 * Get all tomes when the user selects a collection
	 * 
	 * @param contextParam
	 * @param titleCollectionParam
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 */
	public static List<Tome> getAllTomesByCollection(Context contextParam, String titleCollectionParam) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException, DOMException, ParseException {
		
		Document document = loadXmlAsDocument(contextParam);
		XPath xpath = XPathFactory.newInstance().newXPath();
		// Get all tomes of a collection according to the title
		String expression = Constant.COLLECTIONS_NODE + Constant.SEPARATOR_NODE + Constant.COLLECTION_NODE + "[@" + Constant.TITLE_ATTRIBUTE + "='" +  titleCollectionParam + "']/" + Constant.TOME_NODE;
		NodeList nodeList = (NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
		
		List<Tome> listTomes = new ArrayList<Tome>();
		// Create the list of tome from data
		for (int i = 0 ; i < nodeList.getLength() ; i++ ) {
			listTomes.add(new Tome(nodeList.item(i).getAttributes().getNamedItem(Constant.TITLE_ATTRIBUTE).getNodeValue(),
					nodeList.item(i).getAttributes().getNamedItem(Constant.DATE_ATTRIBUTE).getNodeValue(),
					nodeList.item(i).getAttributes().getNamedItem(Constant.PRICE_ATTRIBUTE).getNodeValue(),
					nodeList.item(i).getAttributes().getNamedItem(Constant.OWNED_ATTRIBUTE).getNodeValue()));
		}
		
		return listTomes;
		
	}
	/**
	 * Get a tome with the title of the collection and the title of the tome
	 * 
	 * @param contextParam
	 * @param collectionTitleParam the title of the collection from which we want to retrieve the book
	 * @param tomeTitleParam the title of the tome we want to retrieve
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 * @throws ParseException
	 */
	public static Tome getTome(Context contextParam, String collectionTitleParam, String tomeTitleParam) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException, ParseException {
		Document document = loadXmlAsDocument(contextParam);
		XPath xpath = XPathFactory.newInstance().newXPath();
		// Get the selected tome in the data
		String expression = Constant.COLLECTIONS_NODE + Constant.SEPARATOR_NODE  + Constant.COLLECTION_NODE + "[@" + Constant.TITLE_ATTRIBUTE + "='" +  collectionTitleParam + "']/" + Constant.TOME_NODE + "[@" + Constant.TITLE_ATTRIBUTE + "='" + tomeTitleParam + "']";
		Node node = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
		NamedNodeMap attributes = node.getAttributes();
		Tome tome = new Tome();
		// Create the tome
		for (int i = 0; i < attributes.getLength(); i++) {
			Attr attribute = (Attr)attributes.item(i);
			
			if (attribute.getName().equals(Constant.TITLE_ATTRIBUTE)) {
				tome.setTitle(attribute.getValue());
			}
			
			if (attribute.getName().equals(Constant.PRICE_ATTRIBUTE)) {
				tome.setPrice(Float.parseFloat(attribute.getValue()));
			}
			
			if (attribute.getName().equals(Constant.DATE_ATTRIBUTE)) {
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
				simpleDateFormat.applyPattern(Constant.FORMAT_DATE);
				tome.setPublication_date(simpleDateFormat.parse(attribute.getValue()));
			}
			
			if (attribute.getName().equals(Constant.OWNED_ATTRIBUTE)) {
				tome.setOwned(Boolean.valueOf(attribute.getValue()));
			}
		}
		return tome;
	}
	
	/**
	 * Get the upcoming tomes in the selected month
	 * @param contextParam
	 * @param month the month for which we want the books
	 * @param year the year for which we want the books
	 * @return List<Tome>
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 * @throws ParseException
	 */
	public static List<Tome> getTomeByMonths(Context contextParam, int month, int year) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException, ParseException {
		
		Document document = loadXmlAsDocument(contextParam);
		XPath xpath = XPathFactory.newInstance().newXPath();
		String expression = Constant.COLLECTIONS_NODE + Constant.SEPARATOR_NODE + Constant.COLLECTION_NODE + Constant.SEPARATOR_NODE + Constant.TOME_NODE + "[@" + Constant.DATE_ATTRIBUTE + "]";
		NodeList nodeList = (NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
		
		List<Tome> bookList = new ArrayList<Tome>();
		SimpleDateFormat sdf2 = new SimpleDateFormat(Constant.FORMAT_DATE);
		String owned;
		Date new_date;
		
		Calendar c1 = Calendar.getInstance(); // the current date
		Calendar comparativeDate = Calendar.getInstance(); // the date for which we want the books
		comparativeDate.set(Calendar.YEAR, year);
		comparativeDate.set(Calendar.MONTH, month);
		
		for( int i = 0 ; i < nodeList.getLength() ; i++ ) {	
			new_date=sdf2.parse(nodeList.item(i).getAttributes().getNamedItem(Constant.DATE_ATTRIBUTE).getNodeValue());
			c1.setTime(new_date);
			owned = nodeList.item(i).getAttributes().getNamedItem(Constant.OWNED_ATTRIBUTE).getNodeValue();
			
			// add to the books list the book which match the month and year selected in the spinners and which are not owned
			if( c1.get(Calendar.YEAR) == comparativeDate.get(Calendar.YEAR) && c1.get(Calendar.MONTH) == comparativeDate.get(Calendar.MONTH) && owned.equals("false")) {
				bookList.add( new Tome(nodeList.item(i).getParentNode().getAttributes().getNamedItem(Constant.TITLE_ATTRIBUTE).getNodeValue() + " " + nodeList.item(i).getAttributes().getNamedItem(Constant.TITLE_ATTRIBUTE).getNodeValue(),
								nodeList.item(i).getAttributes().getNamedItem(Constant.DATE_ATTRIBUTE).getNodeValue(),
								nodeList.item(i).getAttributes().getNamedItem(Constant.PRICE_ATTRIBUTE).getNodeValue(),
								nodeList.item(i).getAttributes().getNamedItem(Constant.OWNED_ATTRIBUTE).getNodeValue()
						));				
			}
		}

		return bookList;
		
	}
	
	/**
	 * Add a book to the XML representation of the XML file
	 * @param contextParam the application context
	 * @param tomeTitle the title of the book we want to add to the XML file
	 * @param tomePrice the price of the book we want to add to the XML file
	 * @param tomeOwned the status of the book we want to add to the XML file (owned or not)
	 * @param tomeDate the date of the book we want to add to the XML file
	 * @param collectionTitle the collection where we want to add the book
	 * @return true if the book has correctly been added.
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 * @throws DOMException
	 * @throws ParseException
	 * @throws TransformerException
	 */
	public static boolean addTome(Context contextParam, String tomeTitle, String tomePrice, String tomeOwned, String tomeDate, String collectionTitle ) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException, DOMException, ParseException, TransformerException{
		
		Document document = loadXmlAsDocument(contextParam);
		XPath xpath = XPathFactory.newInstance().newXPath();
		String expression = Constant.COLLECTIONS_NODE + Constant.SEPARATOR_NODE + Constant.COLLECTION_NODE + "[@" + Constant.TITLE_ATTRIBUTE + "='" +  collectionTitle + "']";

		Node node = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);

		Element newtome = document.createElement("tome");
		newtome.setAttribute(Constant.TITLE_ATTRIBUTE, tomeTitle);
		newtome.setAttribute(Constant.DATE_ATTRIBUTE, tomeDate);
		newtome.setAttribute(Constant.PRICE_ATTRIBUTE, tomePrice);
		newtome.setAttribute(Constant.OWNED_ATTRIBUTE, tomeOwned);
		node.appendChild(newtome);

//		Transformer transformer = TransformerFactory.newInstance().newTransformer();
//		Result output = new StreamResult(new File(Constant.MANGALIB_DATA_FILE));
//		Source input = new DOMSource(document);
//		transformer.transform(input, output);

		
		return true;
	}
}
